grammar GodPlGrammar;
options
{
  language = CSharp2;
  output = AST;
  backtrack = true;
  memoize = true;
}


tokens
{
	PROGRAM;
	GLOBALS;
	GLOBAL;
	INSTANCES;
	INSTANCE;
	FUNCTIONS;
	FUNCTION;
	DECLARATION;
	CODE;
	PARAMS;
	ARGS;
	UNARYMINUS;
	ARRAYACCESS;
	FUNCTIONACCESS;
	FIELDACCESS;
	EVENTS;
	EVENT;
	ACTIONS;
	NAME;
	TIMECOMP;
	TIMELIT;
	PARENTHESIZED;
	CONDITIONAL;
	NUMERICLIT;
}

@parser::namespace {GodPl.Parser}
@lexer::namespace {GodPl.Parser} 


program
	:	('globals:'
		globals
		'instances:'
		instances
		'functions:'
		functionDefinitions
		'events:'
		eventDefinitions)
		-> ^(PROGRAM globals instances functionDefinitions eventDefinitions)
		;


ASSIGN 	:	'=';	
PLUS	:	'+';
MINUS	:	'-';
TIMES	:	'*';
DIVIDE	:	'/';
MOD	:	'%';
LESS	:	'<';
GREATER	:	'>';
EQUAL	:	'==';
UNEQUAL	:	'!=';
LEQUAL	:	'<=';
GEQUAL	:	'>=';
OR	:	'||';
AND	:	'&&';
NOT	:	'!';
DOT 	:	'.';
TRUE	:	'true';
FALSE	:	'false';
GAMETIME:	'gameTime';
EVERY	:	'every';
COLON	:	':';
ROLL	:	'roll';
PICKRANDOM
	:	'pickRandom';
IMPLIES :	'->';
VOID	:	'void';


typeSpecifier
	:	primitiveType
	| 	objectCategory
	;
	
primitiveType
	:	'float'
	|	'string'
	;
	
objectCategory
	:	UpperIdentifier
	;

globals
	:	global* -> ^(GLOBALS global*)
	;
	

global
	:	tentativeInitialization -> ^(GLOBAL tentativeInitialization)
	;

instances
	:	instance* -> ^(INSTANCES instance*)
	;

	
instance
	:	tentativeInitialization -> ^(INSTANCE tentativeInitialization)
	;
	

	
tentativeInitializations
	:	tentativeInitialization*
	;
	
functionDefinitions
	:	functionDefinition* -> ^(FUNCTIONS functionDefinition*)
	;
	
eventDefinitions
	:	eventDefinition* -> ^(EVENTS eventDefinition*)
	;
	
eventDefinition
	:	'(' expression ')' IMPLIES '{' actions '}' -> ^(EVENT expression actions)
	; 
	
actions
	:	action* -> ^(ACTIONS action*);
		
action
	:	functionName '(' argumentList ')' ';' -> ^(FUNCTIONACCESS functionName argumentList)
	;

functionDefinition
	:	functionType functionName '(' formalParameterList ')' block ->
		^(FUNCTION functionType ^(NAME functionName) formalParameterList  block)
	;
	
functionType
	: typeSpecifier
	| VOID
	;

	
formalParameterList
	:	(variableDeclaration (',' variableDeclaration)*)? -> ^(PARAMS variableDeclaration*)
	;

variableDeclaration
	:	typeSpecifier identifier -> ^(DECLARATION typeSpecifier identifier)
	;

tentativeInitialization
	:	variableDeclaration ';' -> variableDeclaration
	| 	variableDeclaration ASSIGN expression ';' -> ^(ASSIGN variableDeclaration expression)
	;
	
functionName
	:	identifier
	;
	
block	:	'{' statement* '}' -> ^(CODE statement*)
	;
	
statement
	:	expressionStatement
	|	ifStatement
	|	whileStatement
	|	forEachStatement
	|	breakStatement
	|	returnStatement
	;

expressionStatement
	:	expression ';' -> expression
	;


	
ifStatement
	:	ifPiece elseifPiece* elsePiece? -> ^(CONDITIONAL ifPiece elseifPiece* elsePiece?)
	;
	
ifPiece
	:	'if' '(' expression ')' block -> ^('if' expression block)
	;
	
elseifPiece
	:	'else if' '(' expression ')' block -> ^('else if' expression block)
	;
	
elsePiece
	:	'else' block -> ^('else' block)
	;
	
whileStatement
	:	'while' '(' expression ')' block -> ^('while' expression block)
	;
	
forEachStatement
	:	 'foreach' '(' objectCategory identifier ')' block -> ^('foreach' objectCategory identifier block)
	;
	
breakStatement
	:	'break' ';'!
	;

returnStatement
	:	'return' expression? ';' -> ^('return' expression)
	;

expression
	:	assignmentExpression
	;
	
	
assignmentLeft
	:	variableDeclaration
	|	unaryExpression
	;
	
// Disallowing nested assignments

assignmentExpression
	:	(assignmentLeft ASSIGN orExpression) -> ^(ASSIGN assignmentLeft orExpression)
	|	orExpression
	;
	

	
orExpression
	:	(andExpression -> andExpression) (OR a=andExpression -> ^(OR $orExpression $a) )*
	;
	
andExpression
	:	(equalityExpression -> equalityExpression) (AND a=equalityExpression -> ^(AND $andExpression $a) ) *
	;
	
equalityExpression
	:	(relationalExpression -> relationalExpression)
		(equalityOperator a=relationalExpression -> ^(equalityOperator $equalityExpression $a) ) * 
	;
	


comparisonOperator
	:	equalityOperator | relationalOperator
	;

equalityOperator
	: 	EQUAL | UNEQUAL
	;

relationalOperator
	:	LESS | GREATER | LEQUAL | GEQUAL
	;
		
relationalExpression
	:	(additiveExpression -> additiveExpression)
		(relationalOperator a=additiveExpression -> ^(relationalOperator $relationalExpression $a) )*
	;
	
additiveExpression
	:	(multiplicativeExpression -> multiplicativeExpression)
		(additiveOperator a=multiplicativeExpression -> ^(additiveOperator $additiveExpression $a) )*
	;
	
additiveOperator
	:	PLUS | MINUS
	;
	
multiplicativeExpression
	:	(unaryExpression -> unaryExpression)
		(multiplicativeOperator a=unaryExpression -> ^(multiplicativeOperator $multiplicativeExpression $a) )*
	;
	
multiplicativeOperator
	:	TIMES | DIVIDE | MOD
	;


unaryExpression
	:	postfixExpression
	|  	NOT primaryExpression -> ^(NOT primaryExpression)
	| 	MINUS primaryExpression -> ^(UNARYMINUS primaryExpression)
	;


primaryExpression
	:	identifier -> identifier
	|	numericLiteral -> ^(NUMERICLIT numericLiteral)
	|	StringLiteral
	|	timeExpression
	|	'(' expression ')' -> ^(PARENTHESIZED expression)
	|	booleanLiteral
	|	PICKRANDOM '(' objectCategory ')' -> ^(PICKRANDOM objectCategory)
	|	ROLL '(' expression ')' -> ^(ROLL expression)
	;

//dottedIdentifier
//	:	identifier ('.' identifier)+ -> ^(DOTTED identifier*)
//	;
	
postfixExpression
	:	(primaryExpression -> primaryExpression)
	(	'(' args=argumentList ')' -> ^(FUNCTIONACCESS $postfixExpression $args)
	|	'[' ie=expression ']' -> ^(ARRAYACCESS $postfixExpression $ie)
	|	'.' p=primaryExpression -> ^('.' $postfixExpression $p)
	)*
	;
	
argumentList
	:	(expression (',' expression)*)? -> ^(ARGS expression*)
	;
	
timeExpression
	:	timeEveryExpression
	|	'(' timeCompExpression ')' -> timeCompExpression
	;
	
timeEveryExpression
	:	EVERY '(' TimeLiteral ')' -> ^(EVERY ^(TIMELIT TimeLiteral))
	;

timeCompExpression
	:	GAMETIME comparisonOperator TimeLiteral -> ^(TIMECOMP comparisonOperator ^(TIMELIT TimeLiteral))
	;
	

TimeLiteral
	:	('0'..'9')('0'..'9') ':' ('0'..'9')('0'..'9') ':' ('0'..'9')('0'..'9');
	
	
booleanLiteral
	:	TRUE | FALSE;

numericLiteral
	:	 DecimalLiteral
	;
	
DecimalLiteral
	: DecimalDigit+ ('.' DecimalDigit+)?
	;
	
StringLiteral
	: '"' DoubleStringCharacter* '"'
	;
	
fragment DoubleStringCharacter
	: ~('"' | '\\' |'\n' | '\r')	
	| '\\' EscapeSequence
	;


fragment EscapeSequence
	: CharacterEscapeSequence
	| '0'
	;
	
fragment CharacterEscapeSequence
	: SingleEscapeCharacter
	| NonEscapeCharacter
	;

fragment NonEscapeCharacter
	: ~(EscapeCharacter)
	;

fragment SingleEscapeCharacter
	: '\'' | '"' | '\\' | 'b' | 'f' | 'n' | 'r' | 't' | 'v'
	;

fragment EscapeCharacter
	: SingleEscapeCharacter
	| DecimalDigit
	;
	
fragment DecimalDigit
	: ('0'..'9')
	;

Comment
	: '/*' (options {greedy=false;} : .)* '*/' {$channel=HIDDEN;}
	;

LineComment
	: '//' ~('\n' | '\r')* {$channel=HIDDEN;}
	;

WhiteSpace // Tab, vertical tab, form feed, space, non-breaking space and any other unicode "space separator".
	: ('\t' | '\v' | '\f' | ' ' | '\u00A0' | '\n' | '\r')	{$channel=HIDDEN;}
	;

identifier
	:	LowerIdentifier
	| 	UpperIdentifier
	;

// Starts with lowercase letter
LowerIdentifier
	: ('a'..'z') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')* ;
	
UpperIdentifier
	: ('A'..'Z') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')* ;
